package cn.kow.kmall.controller;

import cn.kow.kmall.common.constant.KmallConstants;
import cn.kow.kmall.common.enums.ItemStatusEnum;
import cn.kow.kmall.common.enums.KmallResultEnum;
import cn.kow.kmall.common.enums.OrderRefundStatusEnum;
import cn.kow.kmall.common.enums.OrderStatusEnum;
import cn.kow.kmall.common.wxpay.WXPayConstants;
import cn.kow.kmall.common.wxpay.WXPayHelper;
import cn.kow.kmall.config.WXAccountConfig;
import cn.kow.kmall.core.exception.ServiceException;
import cn.kow.kmall.core.response.KmallResult;
import cn.kow.kmall.core.util.DateUtils;
import cn.kow.kmall.core.util.JsonUtils;
import cn.kow.kmall.core.util.MoneyUtils;
import cn.kow.kmall.pojo.dto.ItemCardDTO;
import cn.kow.kmall.pojo.dto.OrderQueryDTO;
import cn.kow.kmall.pojo.dto.OrderSubmitDTO;
import cn.kow.kmall.pojo.dto.OrderSubmitItemDTO;
import cn.kow.kmall.pojo.entity.*;
import cn.kow.kmall.pojo.vo.*;
import cn.kow.kmall.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.hibernate.validator.constraints.Range;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotEmpty;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author: lijun
 * @Description:
 * @Date: Created in 2019-05-13 15:27
 * @Modified By:
 */
@Slf4j
@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private ItemService itemService;

    @Autowired
    private AddressService addressService;

    @Autowired
    private OrderService orderService;

    @Autowired
    private OrderRefundService orderRefundService;

    @Autowired
    private CartService cartService;

    @Autowired
    private UserService userService;

    @Autowired
    private WXAccountConfig wxPayConfig;

    /**
     * 下单, 结算页
     * @param uid       uid
     * @param settlementDTO settlementDTO
     * @return
     */
    @PostMapping("/settlement")
    public KmallResult orderSettlement(@RequestAttribute Long uid, @RequestBody OrderSubmitDTO settlementDTO) {
        int fromType = settlementDTO.getFromType();

        // 查询地址
        Long addressId = settlementDTO.getAddressId();
        Address address;
        if (addressId != null) {
            address = addressService.get(uid, addressId);
        } else {
            address = addressService.getDefualt(uid);
        }

        // 查询商品
        List<OrderSubmitItemDTO> submitItemList = settlementDTO.getItems();
        Map<Long, ItemCardDTO> itemMap = getItemMap(submitItemList);

        // 组装订单结算数据
        OrderConfirmVO orderVO = new OrderConfirmVO();

        // 组装地址信息
        if (null != address) {
            OrderAddressVO addressVO = new OrderAddressVO();
            addressVO.setId(address.getId());
            addressVO.setConsignee(address.getName());
            addressVO.setPhone(address.getPhone());
            addressVO.setAddress(address.getProvince() + address.getCity() + address.getCounty() + address.getAddress());
            addressVO.setDefaulted(address.getDefaulted());
            orderVO.setAddress(addressVO);
        }

        // 组装商品数据
        List<OrderItemVO> itemVOList = new ArrayList<>();
        for (OrderSubmitItemDTO submitItem : submitItemList) {
            long itemId = submitItem.getItemId();
            long skuId = submitItem.getSkuId();
            int itemNum = submitItem.getItemNum();
            ItemCardDTO itemDTO = itemMap.get(skuId);
            if (itemDTO == null) {
                throw new ServiceException(KmallConstants.FAIL, "商品" + skuId + "查询失败");
            }
            OrderItemVO item = new OrderItemVO();
            item.setItemId(itemId);
            item.setSkuId(skuId);
            item.setItemNum(itemNum);
            item.setItemSpec(itemDTO.getItemSpec());
            item.setItemName(itemDTO.getItemName());
            item.setItemImage(itemDTO.getItemImage());
            item.setSellingPrice(MoneyUtils.Fen2YuanStr(itemDTO.getSellingPrice()));
            item.setMarketPrice(MoneyUtils.Fen2YuanStr(itemDTO.getMarketPrice()));
            item.setItemStatus(itemDTO.getItemStatus());
            itemVOList.add(item);
        }
        orderVO.setItemList(itemVOList);

        // 组装金额数据
        OrderPriceVO priceInfo = new OrderPriceVO();
        // 订单总金额
        BigDecimal totalAmount = itemVOList.stream().map(i -> new BigDecimal(i.getItemNum()).multiply(new BigDecimal(i.getSellingPrice())))
                .reduce(BigDecimal::add).get();
        int totalNum = itemVOList.stream().mapToInt(i -> i.getItemNum()).sum();
        String totalAmountStr = MoneyUtils.formatCurrency(totalAmount);
        priceInfo.setTotalAmount(totalAmountStr);
        priceInfo.setPayAmount(totalAmountStr);
        priceInfo.setDiscountAmount("0");
        priceInfo.setPostAmount("0");
        priceInfo.setTotalNum(totalNum);
        orderVO.setPriceInfo(priceInfo);

        orderVO.setFromType(fromType);
        return KmallResult.success(orderVO);
    }

    /**
     * 提交订单
     * @param request   请求
     * @param uid       uid
     * @param openid    openid
     * @param submitDTO 下单DTO
     * @return 结果
     * @throws Exception 异常
     */
    @PostMapping("/submit")
    public KmallResult submitOrder(
            HttpServletRequest request,
            @RequestAttribute Long uid,
            @RequestAttribute String openid,
            @RequestBody OrderSubmitDTO submitDTO
    )  throws Exception {
        // 构建订单商品数据
        List<OrderSubmitItemDTO> submitItemList = submitDTO.getItems();
        List<OrderItem> orderItemList = buildOrderItemList(submitItemList);

        // 查询地址
        Long addressId = submitDTO.getAddressId();
        if (addressId == null) {
            return KmallResult.fail("收货地址不能为空");
        }
        Address address = addressService.get(uid, addressId);
        if (address == null) {
            return KmallResult.fail("收货地址不能为空");
        }

        // 查询用户
        User user = userService.getUserByUid(uid);
        if (user == null) {
            return KmallResult.fail("用户不存在");
        }

        // 创建订单
        Order order = buildOrder(user, address, submitDTO, orderItemList);
        orderService.createOrder(order, orderItemList);

        // 购物车删除下单商品
        try {
            Integer fromType = submitDTO.getFromType();
            if (fromType == 0) {
                List<Long> skuIds = orderItemList.stream().map(a -> a.getSkuId()).collect(Collectors.toList());
                cartService.deleteItem(uid, skuIds);
            }
        } catch (Exception e) {
            log.error("createOrder deleteItem ", e);
        }
        return KmallResult.success().put("orderNo", order.getOrderNo());
    }

    private Map<Long, ItemCardDTO> getItemMap(List<OrderSubmitItemDTO> submitItemList) {
        List<Long> skuIds = submitItemList.stream().map(i -> i.getSkuId()).collect(Collectors.toList());
        List<ItemCardDTO> itemList = itemService.listItemCardBySkuIds(skuIds);
        if (CollectionUtils.isEmpty(itemList)) {
            throw new ServiceException(KmallConstants.FAIL, "商品查询失败");
        }
        Map<Long, ItemCardDTO> itemMap = itemList.stream()
                .collect(Collectors.toMap(ItemCardDTO::getSkuId, a -> a));
        return itemMap;
    }

    private List<OrderItem> buildOrderItemList(List<OrderSubmitItemDTO> submitItemList) throws ServiceException {
        if (CollectionUtils.isEmpty(submitItemList)) {
            throw new ServiceException(KmallConstants.FAIL, "商品不能为空");
        }

        // 查询商品
        Map<Long, ItemCardDTO> itemMap = getItemMap(submitItemList);
        List<OrderItem> orderItemList = new ArrayList<>();
        for (OrderSubmitItemDTO submitItem : submitItemList) {
            int itemNum = submitItem.getItemNum();
            long skuId = submitItem.getSkuId();
            long itemId = submitItem.getItemId();

            ItemCardDTO item = itemMap.get(skuId);
            if (item == null) {
                throw new ServiceException(KmallConstants.FAIL, "商品" + skuId + "不存在");
            }
            if (!ItemStatusEnum.ONLINE.getValue().equals(item.getItemStatus())) {
                throw new ServiceException(KmallConstants.FAIL, String.format("商品%s已下架", itemId));
            }

            Integer unitPrice = item.getSellingPrice();
            Integer totalFee = unitPrice * itemNum;

            OrderItem orderItem = new OrderItem();
            orderItem.setItemId(itemId);
            orderItem.setSkuId(skuId);
            orderItem.setBuyNum(itemNum);
            orderItem.setUnitPrice(unitPrice);
            orderItem.setTotalFee(totalFee);
            orderItem.setPayFee(totalFee);
            orderItem.setItemName(item.getItemName());
            orderItem.setItemSpec(item.getItemSpec());
            orderItem.setItemPic(item.getItemImage());
            orderItemList.add(orderItem);
        }
        return orderItemList;
    }

    private Order buildOrder(User user, Address address, OrderSubmitDTO submitDTO, List<OrderItem> orderItemList) {
        // 地址
        String receiverName = address.getName();
        String receiverPhone = address.getPhone();
        String receiverAddress = address.getProvince() + address.getCity() + address.getCounty() + address.getAddress();

        // 价格计算
        int totalNum = 0;
        int totalFee = 0;
        for (OrderItem orderItem : orderItemList) {
            totalNum += orderItem.getBuyNum();
            totalFee += orderItem.getTotalFee();
        }
        int postFee = 0;
        int couponFee = 0;
        int discountFee = 0;
        int payFee = totalFee + postFee - couponFee - discountFee;

        Order order = new Order();
        order.setMessage(submitDTO.getBuyerMessage());
        order.setUid(user.getUid());
        order.setOrderType(1);
        order.setOrderStatus(OrderStatusEnum.CREATE.getValue());
        order.setPayCode("WXPAY");
        order.setTotalNum(totalNum);
        order.setTotalFee(totalFee);
        order.setPayFee(payFee);
        order.setDiscountFee(discountFee);
        order.setCouponFee(couponFee);
        order.setPostFee(postFee);
        order.setReceiverName(receiverName);
        order.setReceiverPhone(receiverPhone);
        order.setReceiverAddress(receiverAddress);
        return order;
    }

    /**
     * 发起支付
     * @param request   请求
     * @param uid       uid
     * @param openid    openid
     * @param orderNo   订单号
     * @return 支付信息
     * @throws Exception 异常
     */
    @GetMapping(value = "/pay")
    public KmallResult pay(
            HttpServletRequest request,
            @RequestAttribute Long uid,
            @RequestAttribute String openid,
            @NotEmpty(message = "订单号不能为空")
            @RequestParam String orderNo
    ) throws Exception {
        Order order = orderService.getOrder(orderNo);
        List<Integer> validStatus = Arrays.asList(OrderStatusEnum.CREATE.getValue());
        checkOrder(order, uid, validStatus);

        // 微信统一下单
        Map<String, String> payResponse = WXPayHelper.unifiedOrder(request, openid, order, wxPayConfig);

        OrderVO orderVO = toOrderVO(order);
        return KmallResult.success().put("order", orderVO).put("pay", payResponse);
    }

    /**
     * 取消订单
     * @param uid       uid
     * @param orderNo   订单号
     * @return 支付信息
     * @throws Exception 异常
     */
    @GetMapping(value = "/cancel")
    public KmallResult cancel(
            @RequestAttribute Long uid,
            @NotEmpty(message = "订单号不能为空")
            @RequestParam String orderNo
    ) throws Exception {
        Order order = orderService.getOrder(orderNo);
        List<Integer> validStatus = Arrays.asList(OrderStatusEnum.CREATE.getValue(), OrderStatusEnum.PAYED.getValue());
        int orderStatus = order.getOrderStatus();
        checkOrder(order, uid, validStatus);

        // 取消订单
        order.setOrderStatus(OrderStatusEnum.CLOSE.getValue());
        order.setCancelTime(new Date());
        orderService.updateOrder(order);

        // 退款
        if (OrderStatusEnum.PAYED.getValue() == orderStatus) {
            Integer payAmount = order.getPayFee();
            if (payAmount > 0) {
                OrderRefund orderRefund = new OrderRefund();
                orderRefund.setOrderNo(order.getOrderNo());
                orderRefund.setUid(order.getUid());
                orderRefund.setAmount(payAmount);
                orderRefund.setChannel("WXPAY");
                orderRefund.setRefundStatus(OrderRefundStatusEnum.INIT.getValue());
                orderRefund.setRefundDesc("取消订单");
                orderRefund = orderRefundService.create(orderRefund);
                Map<String, String> refundResponse = WXPayHelper.refund(order.getOrderNo(), orderRefund.getRefundNo(), payAmount, wxPayConfig);
                log.info(JsonUtils.toJson(refundResponse));
                String returnCode = refundResponse.get("result_code");
                if (WXPayConstants.FAIL.equals(returnCode)) {
                    log.info("微信退款失败");
                    String errMsg = refundResponse.get("err_code_des");
                    orderRefund.setErrMsg(errMsg);
                    orderRefund.setRefundStatus(OrderRefundStatusEnum.FAIL.getValue());
                } else {
                    String outRefundNo = refundResponse.get("refund_id");
                    orderRefund.setOutRefundNo(outRefundNo);
                    orderRefund.setRefundStatus(OrderRefundStatusEnum.SUCCESS.getValue());
                    orderRefund.setSuccessTime(new Date());
                }
                orderRefund.setRemark(JsonUtils.toJson(refundResponse, false));
                orderRefundService.update(orderRefund);
            }
        }

        return KmallResult.success();
    }

    /**
     * 确认收货
     * @param uid       uid
     * @param orderNo   订单号
     * @return 支付信息
     * @throws Exception 异常
     */
    @GetMapping(value = "/confirm")
    public KmallResult confirm(
            @RequestAttribute Long uid,
            @NotEmpty(message = "订单号不能为空")
            @RequestParam String orderNo) {
        Order order = orderService.getOrder(orderNo);
        checkOrder(order, uid, Arrays.asList(OrderStatusEnum.SEND.getValue()));

        order.setOrderStatus(OrderStatusEnum.COMPLETE.getValue());
        order.setConfirmTime(new Date());
        orderService.updateOrder(order);
        return KmallResult.success();
    }

    /**
     * 删除订单
     * @param uid       uid
     * @param orderNo   订单号
     * @return 支付信息
     * @throws Exception 异常
     */
    @GetMapping(value = "/delete")
    public KmallResult delete(
            @RequestAttribute Long uid,
            @NotEmpty(message = "订单号不能为空")
            @RequestParam String orderNo) {
        Order order = orderService.getOrder(orderNo);
        checkOrder(order, uid, Arrays.asList(OrderStatusEnum.COMPLETE.getValue(), OrderStatusEnum.CLOSE.getValue()));
        order.setDeleted(true);
        orderService.updateOrder(order);
        return KmallResult.success();
    }

    /**
     * 商品订单列表
     *
     * @param uid
     * @param type  0全部，1待付款，2待发货，3待收货, 4待评价
     * @param pageable
     * @return
     */
    @GetMapping("/list")
    public KmallResult orderList(
            @RequestAttribute Long uid,
            @Range(max = 5, message = "type值错误")
            @RequestParam(defaultValue = "0") Integer type,
            @PageableDefault Pageable pageable) {
        OrderQueryDTO queryDTO = new OrderQueryDTO();
        queryDTO.setUid(uid);
        queryDTO.setPage(pageable.getPageNumber());
        queryDTO.setSize(pageable.getPageSize());
        if (type > 0) {
            queryDTO.setOrderStatus(type);
        }
        Page<Order> orderPage = orderService.listByPage(queryDTO);
        List<OrderVO> orderVOList = new ArrayList<>();

        if (orderPage.hasContent()) {
            for (Order order : orderPage.getContent()) {
                List<OrderItem> itemList = orderService.listOrderItemByLimit(order.getOrderNo(), 3);

                OrderVO orderVO = toOrderVO(order);
                List<OrderItemVO> itemVOList = new ArrayList<>();

                for (OrderItem orderItem : itemList) {
                    OrderItemVO itemVO = toOrderItemVO(orderItem);
                    itemVOList.add(itemVO);
                }
                orderVO.setItemList(itemVOList);
                orderVOList.add(orderVO);
            }
        }

        return KmallResult.success().put("list", orderVOList)
                .put("type", type)
                .put("page", pageable.getPageNumber())
                .put("size", pageable.getPageSize())
                .put("hasMore", orderPage.hasNext());
    }

    /**
     * 商品订单详情
     * @param uid
     * @param orderNo
     * @return
     */
    @GetMapping("/info")
    public KmallResult orderInfo(
            @RequestAttribute Long uid,
            @NotEmpty(message = "订单号不能为空")
            @RequestParam String orderNo
    ) {
        Order order = orderService.getOrder(orderNo);

        // 订单
        OrderVO orderVO = toOrderVO(order);

        // 订单商品
        List<OrderItem> itemList = orderService.listOrderItem(order.getOrderNo());
        List<OrderItemVO> itemVOList = new ArrayList<>();
        for (OrderItem orderItem : itemList) {
            OrderItemVO itemVO = toOrderItemVO(orderItem);
            itemVOList.add(itemVO);
        }
        orderVO.setItemList(itemVOList);
        return KmallResult.success(orderVO);
    }

    /**
     * 取消订单
     * @param uid       uid
     * @param orderNo   订单号
     * @return 支付信息
     * @throws Exception 异常
     */
    @GetMapping(value = "/logistics/track")
    public KmallResult logisticsTrack(
            @RequestAttribute Long uid,
            @NotEmpty(message = "订单号不能为空")
            @RequestParam String orderNo
    ) throws Exception {
        Order order = orderService.getOrder(orderNo);
        List<Integer> validStatus = Arrays.asList(OrderStatusEnum.SEND.getValue(), OrderStatusEnum.COMPLETE.getValue());
        int orderStatus = order.getOrderStatus();
        checkOrder(order, uid, validStatus);

        // 查询物流轨迹
        String logisticsNo = order.getLogisticsNo();
        List<Map<String, String>> trackList = new ArrayList<>();
        Map<String, String> track1 = new HashMap<>();
        track1.put("context", "订单提交，系统确认中");
        track1.put("time", DateUtils.format(order.getPayTime()));
        track1.put("color", "grey");
        trackList.add(track1);

        Map<String, String> track2 = new HashMap<>();
        track2.put("context", "订单开始处理，请您耐心等待");
        track2.put("time", DateUtils.format(DateUtils.addHours(order.getPayTime(), 1)));
        track1.put("color", "grey");
        trackList.add(track2);

        Map<String, String> track3 = new HashMap<>();
        track3.put("context", "打包完成");
        track3.put("time", DateUtils.format(order.getSendTime()));
        track1.put("color", "grey");
        trackList.add(track3);

        Map<String, String> track4 = new HashMap<>();
        track4.put("context", "运输中");
        track4.put("time", DateUtils.format(DateUtils.addHours(order.getSendTime(), 2)));
        track1.put("color", "grey");
        trackList.add(track4);

        if (OrderStatusEnum.COMPLETE.getValue().equals(orderStatus)) {
            Map<String, String> track5 = new HashMap<>();
            track5.put("context", "已签收");
            track5.put("time", DateUtils.format(order.getConfirmTime()));
            track1.put("color", "red");
            trackList.add(track5);
        }
        Collections.reverse(trackList);
        return KmallResult.success().put("trackList", trackList).put("isFinish", OrderStatusEnum.COMPLETE.getValue().equals(orderStatus));
    }

    /**
     * 检查订单
     * @param order     订单
     * @param uid       创建人
     * @param validStatus   有效状态
     */
    private void checkOrder(Order order, Long uid, List<Integer> validStatus) {
        if (order == null) {
            throw new ServiceException(KmallResultEnum.ORDER_NOT_EXIST);
        }
        Integer orderStatus = order.getOrderStatus();
        if (!validStatus.contains(orderStatus)) {
            throw new ServiceException(KmallResultEnum.ORDER_STATUS_ERROR);
        }
        if (!order.getUid().equals(uid)) {
            throw new ServiceException(KmallResultEnum.ORDER_STATUS_ERROR);
        }
    }

    /**
     * 组装orderVO
     * @param order 订单
     * @return VO
     */
    private OrderVO toOrderVO(Order order) {
        OrderVO orderVO = new OrderVO();
        orderVO.setOrderNo(order.getOrderNo());
        orderVO.setFreight(MoneyUtils.Fen2YuanStr(order.getPostFee()));
        orderVO.setTotalAmount(MoneyUtils.Fen2YuanStr(order.getTotalFee()));
        orderVO.setDiscountAmount(MoneyUtils.Fen2YuanStr(order.getDiscountFee()));
        orderVO.setCouponAmount(MoneyUtils.Fen2YuanStr(order.getCouponFee()));
        orderVO.setPayAmount(MoneyUtils.Fen2YuanStr(order.getPayFee()));
        orderVO.setTotalNum(order.getTotalNum());
        orderVO.setOrderStatus(OrderStatusEnum.getMap().get(order.getOrderStatus()));
        orderVO.setCreateTime(order.getCreateTime());
        orderVO.setPayTime(order.getPayTime());
        orderVO.setSendTime(order.getSendTime());
        orderVO.setConfirmTime(order.getConfirmTime());
        orderVO.setCancelTime(order.getCancelTime());
        orderVO.setConsignee(order.getReceiverName());
        orderVO.setPhone(order.getReceiverPhone());
        orderVO.setAddress(order.getReceiverAddress());
        return orderVO;
    }

    /**
     * 组装orderVO
     * @param orderItem 订单
     * @return VO
     */
    private OrderItemVO toOrderItemVO(OrderItem orderItem) {
        OrderItemVO itemVO = new OrderItemVO();
        itemVO.setItemId(orderItem.getItemId());
        itemVO.setSkuId(orderItem.getSkuId());
        itemVO.setItemNum(orderItem.getBuyNum());
        itemVO.setItemName(orderItem.getItemName());
        itemVO.setItemImage(orderItem.getItemPic());
        itemVO.setSellingPrice(MoneyUtils.Fen2YuanStr(orderItem.getUnitPrice()));
        itemVO.setItemSpec(orderItem.getItemSpec());
        return itemVO;
    }
}
